import cherrypy
import requests
import shelve
import sys
import threading
import time
import math

db_path = "./postvotes"
tor_exit_nodes_url = "https://www.dan.me.uk/torlist/?exit"

class GoodPost:
    def __init__(self, db, tor_exit_nodes_url):
        self.db = db
        self.tor_exit_nodes_url = tor_exit_nodes_url
        self.exit_nodes = set() # Empty set
        self.cache_update = 0
        self.cache = ""
        self.lock = threading.Lock()
        self.stats_template = """
        <!doctype html>
        <html>
         <head><title>pcworms posts rankings</title></head>
         <body>
            <table>(*ROWS_HERE*)</table>
         </body>
        </html>
        """
        threading.Timer(1, self.update_exit_nodes_list)
   
    def update_exit_nodes_list(self):
        response = requests.get(self.tor_exit_nodes_url)
        self.exit_nodes = set(response.text.split())
        threading.Timer(30 * 60, self.update_exit_nodes) # 30min

    def update_cache(self):
        passed_time = time.time() - self.cache_update
        if passed_time >= 60: # 60sec
            posts_scores = []
            for post_url in self.db.keys():
                pos = len(self.db[post_url]["1"])
                total = pos + len(self.db[post_url]["0"])
                score = self.calcscore(pos, total)
                posts_scores.append((post_url, score))
            
            posts_scores.sort(key=lambda x: x[1])
            rows = ""
            row_template= '<tr><td><a href="%s">%s</a></td><td>%f</td></tr>'
            for post_url, score in posts_scores:
                rows += row_template % (post_url, post_url, score)
            
            self.cache = self.stats_template.replace("(*ROWS_HERE*)", rows)


    def calcscore(self, pos, total):
        # https://www.evanmiller.org/how-not-to-sort-by-average-rating.html
        confidence = .95
        z = 1.96
        if not total:
            return 0

        phat = pos/total
        sqr = math.sqrt((phat*(1-phat) + z**2/4/total) / total)
        return (phat + z*z/2/total  - z * sqr) / (1 + z*z/total)

            

    @cherrypy.expose
    def index(self, vote=""):
        if vote not in ("1", "0"):
            return "Incorrect vote. Vote should be either 0 or 1"

        if "Referer" not in cherrypy.request.headers:
            return "You are not coming from anywhere..."

        post_url = cherrypy.request.headers["Referer"]
        if post_url.split("/")[2] != "pcworms.blog.ir":
            return "You are not coming from pcworms.blog.ir"

        ip = cherrypy.request.remote.ip
        if ip in self.exit_nodes:
            return "Sorry! You are from TOR!"
        
        with self.lock:
            if post_url in self.db.keys():
                self.db[post_url][vote].add(ip)
            else:
                self.db[post_url] = dict()
                self.db[post_url]["1"] = set()
                self.db[post_url]["0"] = set()
                self.db[post_url][vote].add(ip)
            self.db.sync()
        return "Thanks for voting for this article!"

    @cherrypy.expose
    def stats(self):
        self.update_cache()
        return self.cache

    @cherrypy.expose
    def potato(self):
        return "Hello! I am a Potato!"

if __name__ == "__main__":
    conf = {
        "global":
        {
            "server.socket_host": "0.0.0.0",
            "server.socket_port": 2051,
        }
    }
    with shelve.open(db_path, writeback=True, flag='c') as db:
        cherrypy.quickstart(GoodPost(db, tor_exit_nodes_url), "/", conf)
